<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

    <title>Gin学习笔记</title>
    <link rel="shortcut icon" href="./favicon.ico" />
    <link rel="stylesheet" href="./dist/reset.css" />
    <link rel="stylesheet" href="./dist/reveal.css" />
    <link rel="stylesheet" href="./dist/theme/black.css" id="theme" />
    <link rel="stylesheet" href="./css/highlight/base16/zenburn.css" />


  </head>
  <body>
    <div class="reveal">
      <div class="slides"><section  data-markdown><script type="text/template">

Gin是基于Go开发的Web微框架，相当简洁好用。


</script></section><section  data-markdown><script type="text/template">


# Download Gin

```bash
go get -u -v github.com/gin-gonic/gin
```


</script></section><section ><section data-markdown><script type="text/template">


# Usage

需要 `import "github.com/gin-gonic/gin"`。


</script></section><section data-markdown><script type="text/template">


## 创建

- `gin.Default()`

生成一个gin实例。


</script></section><section data-markdown><script type="text/template">


## 接收请求

HTTP请求中有**GET，POST，PUT，PATCH，DELETE**和**OPTIONS**等方法，还有一个**Any**可以匹配所有方法。

- `GET(path, func)`

<font size=2>声明一个“路由”（即被请求的路径`path`），当客户端（浏览器）使用HTTP的GET方法向服务器请求位于`path`的页面时，触发func所定义的函数进行处理。这个func只有一个参数，即`gin.Context`类型的指针，这个指针指向的地址空间储存了一些对传来的HTTP报文解析后的信息，解析过程是gin封装好的。`POST(path, func)` 等也同理。</font>


</script></section><section data-markdown><script type="text/template">


此外，Gin还支持分组路由功能。例如有一组路由均为`/a`打头，则可以写为：

```go[1|3-4]
a := r.Group("/a")
{
	v1.GET("/b", defaultHandler) // GET /a/b
	v1.GET("/c", defaultHandler) // GET /a/c
}
```


</script></section><section data-markdown><script type="text/template">

## 数据收集

有时请求的URL中会承载一定的信息，这部分信息可以使用`Param()`或`Query()`获取。

- `Param(string)`

如果要使用`Param()`方法，则声明的URL中需要包含对应的“占位符”（不清楚学名，笔者自己这么称呼的），用冒号来表示。例如：

```go
r.GET("/user/:name", func(c *gin.Context) {
	name := c.Param("name")
	c.String(http.StatusOK, "hello, %s", name)
})
```

此时，如果浏览器向服务器请求`"/user/dzc"`，则服务端会返回字符串`"hello, dzc"`。

- `Query(string)`

如果要使用`Query()`方法，则实际请求的URL中需要包含问号`?`，将需要发送到服务端的信息以键值对的形式放在问号后面，键值对之间使用`&`分隔。例如：

```go
r.GET("/welcome", func(c *gin.Context) {
	first := c.Query("first")
	last := c.Query("last")
	out := "hello, " + first + last
	c.String(http.StatusOK, out)
})
```

此时，如果浏览器向服务器请求`"/welcome?first=d&last=zc"`，则服务器回返回字符串`hello, dzc`。

另一些时候，浏览器的数据会以POST方法发送给服务器，这部分数据可以使用`PostForm()`来解析。

- `PostForm(string)` & `DefaultPostForm(string, string)`

用POST方法提交的表单数据也是以键值对的形式表示的，可以使用`PostForm()`将其解析出来，而该方法的另一个变种`DefaultPostForm()`则允许使用者在未解析到所需字段时设置一个默认结果，例如：

```go
r.POST("/register", func(c *gin.Context) {
	login := c.PostForm("login")
	passwd := c.DefaultPostForm("pass","defaultpasswd")
	if login == "3200104203" && passwd == "4203" {
		c.String(http.StatusOK, "Login successful.")
	} else {
		c.String(http.StatusOK, "Wrong password.")
	}
})
```
</script></section><section data-markdown><script type="text/template">
## 返回数据

Gin支持返回多种类型的数据，如字符串、JSON、HTML页面等。

- `String(int, string)`

`String()`用于返回一个字符串，第一个参数是返回的HTTP状态码，第二个参数是要返回的字符串内容。例如：`c.String(http.StatusOK, "hello world")`。

- `JSON(int, gin.H)`

`JSON()`用于返回一个JSON对象，第一个参数是返回的HTTP状态码，第二个参数是要返回的JSON内容。`gin.H`类型实际上就是`map[string]interface{}`，其中空接口可以代表任何类型（所有类型均视为实现了一个空接口），所以`gin.H`类的实例相当于一个JSON对象。

- `HTML(int, string, gin.H)`

`HTML()`用于返回一个HTML页面，第一个参数是返回的HYTTP状态码，第二个参数是要返回的页面的路径，第三个参数是需要嵌入到HTML中的数据（如果HTML中出现`{{.msg}}`，即Gin的模板语法，则会用`"this is a message from server."`代替）。在使用这个函数之前，需要用`LoadHTMLGlob()`或`LoadHTMLFiles()`将HTML页面文件从硬盘加载进内存中，其中前者可以一次加载整个目录下的所有文件，而后者则单独加载某一个文件。例如：

```go
r.LoadHTMLFiles("root/index.html") // 或 r.LoadHTMLGlob("root/*")
r.GET("/reg", func(c *gin.Context) {
    c.HTML(http.StatusOK, "index.html", gin.H{
        "msg": "this is a message from server.",
    })
})
```
</script></section><section data-markdown><script type="text/template">
## 中间件

中间件，顾名思义就是在浏览器和服务器中间的一层东西。而这层东西可以对浏览器发来的请求进行拦截并进行一些预处理，例如权限验证等。此外，中间件还可以在服务器完成处理后、向客户端发送响应前进行一些处理（如添加统一的响应头等）。Gin有内置一些中间件，如默认使用的`Logger()`和`Recovery()`。

- 全局使用中间件`Use(func)`

`Use()`方法用于全局使用中间件。例如：`r.Use(gin.Recovery())`

- 路由分组使用中间件

在创建路由分组时可以添加该路由分组使用的中间件。

例如：`user := router.Group("user",gin.Recovery())` 。

- 单个路由使用中间件

在创建单个路由时也可以添加该路由使用的中间件。

例如：`r.GET("/",gin.Recovery(),DefaultHandler)` 。

- 自定义中间件

Gin规范了自定义中间件的方式：

```go
func MyMiddleware(c *gin.Context){
    
}
```

在自定义中间件时，如果需要与服务端进行**数据传递**的话，可以使用`Set()`和`Get()`方法。

- `Set(string, interface{})`

使用`Set()`时，相当于为`gin.Context`设置了一个键值对。其中键必须是`string`类型的，而值可以是任意类型的。

- `Get(string)`

使用`Get()`可以将之前`Set()`的值读取出来，其返回两个值，第一个是键对应的值，第二个是该键是否存在，用布尔类型表示。

`Next()`方法可以划分中间件的前置和后置功能。`Next()`调用前的代码将在请求到达服务端之前进行，而`Next()`调用后的代码则会在服务端处理完毕后、正式向客户端发送响应前运行。

`Abort()`方法可以拦截请求/响应。

### 示例：

```go
MyMiddlewareForAuth := func(c *gin.Context) {
	login := c.PostForm("login")
	passwd := c.PostForm("pass")
	if login == "3200104203" && passwd == "4203" {
		c.Set("Authres", 1)
	} else {
		c.Set("Authres", 0)
		c.Next()
		c.String(http.StatusOK, "Wrong password.")
	}
}

r.POST("/register", MyMiddlewareForAuth, func(c *gin.Context) {
	if val, exists := c.Get("Authres"); exists && val == 1 {
		c.String(http.StatusOK, "Login successful.")
	} else {
		c.String(http.StatusOK, "hello?\n")
	}
})
```
</script></section></section><section  data-markdown><script type="text/template">
# Reference

https://geektutu.com/post/quick-go-gin.html

https://zhuanlan.zhihu.com/p/151818857

https://cloud.tencent.com/developer/article/1585029
</script></section><section  data-markdown><script type="text/template">
# 有的没的

## 没事听点歌（Billie Eilish - bad guy）

<audio controls>
    <source src = "https://cyclohexatriene.github.io/music/bad guy.mp3">
</audio>


</script></section></div>
    </div>

    <script src="./dist/reveal.js"></script>

    <script src="./plugin/markdown/markdown.js"></script>
    <script src="./plugin/highlight/highlight.js"></script>
    <script src="./plugin/zoom/zoom.js"></script>
    <script src="./plugin/notes/notes.js"></script>
    <script src="./plugin/math/math.js"></script>
    <script>
      function extend() {
        var target = {};
        for (var i = 0; i < arguments.length; i++) {
          var source = arguments[i];
          for (var key in source) {
            if (source.hasOwnProperty(key)) {
              target[key] = source[key];
            }
          }
        }
        return target;
      }

      // default options to init reveal.js
      var defaultOptions = {
        controls: true,
        progress: true,
        history: true,
        center: true,
        transition: 'default', // none/fade/slide/convex/concave/zoom
        slideNumber: true,
        plugins: [
          RevealMarkdown,
          RevealHighlight,
          RevealZoom,
          RevealNotes,
          RevealMath
        ]
      };

      // options from URL query string
      var queryOptions = Reveal().getQueryHash() || {};

      var options = extend(defaultOptions, {}, queryOptions);
    </script>


    <script>
      Reveal.initialize(options);
    </script>
  </body>
</html>
